import requests
import time
import re
from concurrent.futures import ThreadPoolExecutor, as_completed
from flask import Flask, Response
import logging
import subprocess
import eventlet

app = Flask(__name__)
logging.basicConfig(level=logging.INFO)

# 配置帧率阈值
FPS_THRESHOLD = 22

def is_ipv6_address(url):
    ipv6_pattern = r'\[[0-9a-fA-F:]+\]'
    return re.search(ipv6_pattern, url) is not None

def download_m3u8_content(url):
    try:
        response = requests.get(url)
        response.raise_for_status()
        return response.text
    except requests.RequestException as e:
        logging.error(f"Failed to download M3U8 file from {url}: {e}")
        raise

def normalize_tvg_name(tvg_name):
    return tvg_name.replace('-', '')

def calculate_average_fps(url):
    command = [
        'ffmpeg',
        '-i', url,
        '-t', '3',
        '-f', 'null', '-',
        '-v', 'error',
        '-stats'
    ]

    try:
        result = subprocess.run(command, capture_output=True, text=True, timeout=15)
        output = result.stderr

        frame_time_matches = re.findall(r'frame=\s*(\d+).*?time=([\d:.]+)', output)

        if frame_time_matches:
            total_frames = 0
            total_time_seconds = 0.0

            for match in frame_time_matches:
                frames = int(match[0])
                time_str = match[1]
                time_parts = list(map(float, time_str.split(':')))
                time_seconds = time_parts[0] * 3600 + time_parts[1] * 60 + time_parts[2]

                total_frames = frames
                total_time_seconds = time_seconds

            if total_time_seconds > 0:
                average_fps = total_frames / total_time_seconds
                return average_fps
            else:
                return None

        else:
            return None

    except subprocess.TimeoutExpired:
        return None
    except Exception as e:
        logging.error(f"An error occurred: {e}")
        return None

def check_iptv_source(m3u8_content, backup_m3u8_urls):
    lines = m3u8_content.splitlines()
    new_m3u8_content = []
    i = 0
    while i < len(lines):
        line = lines[i]
        if line.startswith('#EXTINF'):
            match = re.search(r'tvg-name="([^"]+)"', line)
            tvg_name = match.group(1) if match else "Unknown"
            i += 1
            url = lines[i]
            if is_ipv6_address(url):
                logging.info(f"电视台: {tvg_name}, URL: {url} contains an IPv6 address, skipping.")
                new_m3u8_content.append(line)
                new_m3u8_content.append(url)
            else:
                if not check_url(tvg_name, url):
                    logging.info(f"电视台: {tvg_name}, URL: {url} is not reachable. Searching for a replacement...")
                    replacement_url, response_time = find_fastest_replacement(normalize_tvg_name(tvg_name), backup_m3u8_urls)
                    if replacement_url:
                        logging.info(f"Replacement found for {tvg_name}: {replacement_url} (Response time: {response_time:.2f} seconds)")
                        new_m3u8_content.append(line)
                        new_m3u8_content.append(replacement_url)
                    else:
                        logging.info(f"No replacement found for {tvg_name}.")
                        new_m3u8_content.append(line)
                        new_m3u8_content.append(url)
                else:
                    new_m3u8_content.append(line)
                    new_m3u8_content.append(url)
        elif ',' in line:
            tvg_name, url = line.rsplit(',', 1)
            if is_ipv6_address(url):
                logging.info(f"电视台: {tvg_name}, URL: {url} contains an IPv6 address, skipping.")
                new_m3u8_content.append(line)
            else:
                if not check_url(tvg_name, url):
                    logging.info(f"电视台: {tvg_name}, URL: {url} is not reachable. Searching for a replacement...")
                    replacement_url, response_time = find_fastest_replacement(normalize_tvg_name(tvg_name), backup_m3u8_urls)
                    if replacement_url:
                        logging.info(f"Replacement found for {tvg_name}: {replacement_url} (Response time: {response_time:.2f} seconds)")
                        new_m3u8_content.append(f"{tvg_name},{replacement_url}")
                    else:
                        logging.info(f"No replacement found for {tvg_name}.")
                        new_m3u8_content.append(line)
                else:
                    new_m3u8_content.append(line)
        i += 1
    return "\n".join(new_m3u8_content)

def check_url(tvg_name, url):
    try:
        start_time = time.time()
        response = requests.head(url, allow_redirects=False, timeout=10)  # 增加超时时间
        end_time = time.time()
        response_time = end_time - start_time
        if response.status_code in [200, 302]:
            logging.info(f"电视台: {tvg_name}, URL: {url} is OK. Response time: {response_time:.2f} seconds")
            average_fps = calculate_average_fps(url)
            if average_fps is not None and average_fps >= FPS_THRESHOLD:
                logging.info(f"电视台: {tvg_name}, URL: {url} has an average FPS of {average_fps:.2f}")
                return True
            else:
                logging.info(f"电视台: {tvg_name}, URL: {url} has a low average FPS of {average_fps if average_fps is not None else 'N/A'}")
                return False
    except requests.RequestException as e:
        logging.error(f"Error checking URL {url}: {e}")
        return False

def check_backup_url(tvg_name, url):
    try:
        start_time = time.time()
        with eventlet.Timeout(10, False):  # 增加超时时间
            response = requests.head(url, allow_redirects=False, timeout=5)
        end_time = time.time()
        response_time = end_time - start_time
        if response.status_code in [200, 302]:
            average_fps = calculate_average_fps(url)
            if average_fps is not None and average_fps >= FPS_THRESHOLD:
                logging.info(f"电视台: {tvg_name}, URL: {url} has an average FPS of {average_fps:.2f}")
                return url, response_time
            else:
                logging.info(f"电视台: {tvg_name}, URL: {url} has a low average FPS of {average_fps if average_fps is not None else 'N/A'}")
                return None, float('inf')
        else:
            return None, float('inf')
    except eventlet.timeout.Timeout:
        logging.error(f"Timeout checking backup URL {url} for {tvg_name}")
        return None, float('inf')
    except requests.RequestException as e:
        logging.error(f"Error checking backup URL {url} for {tvg_name}: {e}")
        return None, float('inf')

def find_fastest_replacement(normalized_tvg_name, backup_m3u8_urls):
    fastest_url = None
    fastest_time = float('inf')
    with ThreadPoolExecutor(max_workers=10) as executor:
        future_to_url = {}
        for backup_url in backup_m3u8_urls:
            try:
                backup_content = download_m3u8_content(backup_url)
                lines = backup_content.splitlines()
                for line in lines:
                    if line.startswith('#EXTINF'):
                        match = re.search(r'tvg-name="([^"]+)"', line)
                        if match and normalize_tvg_name(match.group(1)) == normalized_tvg_name:
                            next_line = lines[lines.index(line) + 1]
                            if not is_ipv6_address(next_line):
                                future = executor.submit(check_backup_url, normalized_tvg_name, next_line)
                                future_to_url[future] = next_line
                        elif 'tvg-name="' not in line:
                            match = re.search(r'tvg-name="([^"]+)"', lines[lines.index(line) - 1])
                            if match and normalize_tvg_name(match.group(1)) == normalized_tvg_name:
                                next_line = lines[lines.index(line) + 1]
                                if not is_ipv6_address(next_line):
                                    future = executor.submit(check_backup_url, normalized_tvg_name, next_line)
                                    future_to_url[future] = next_line
                    elif ',' in line:
                        backup_tvg_name, backup_url = line.rsplit(',', 1)
                        if normalize_tvg_name(backup_tvg_name) == normalized_tvg_name:
                            if not is_ipv6_address(backup_url):
                                future = executor.submit(check_backup_url, normalized_tvg_name, backup_url)
                                future_to_url[future] = backup_url
            except Exception as e:
                logging.error(f"An error occurred with backup URL {backup_url}: {e}")
                continue

        for future in as_completed(future_to_url):
            url, response_time = future.result()
            if url and response_time < fastest_time:
                fastest_url = url
                fastest_time = response_time
    return fastest_url, fastest_time

def save_m3u8_file(content, filename):
    with open(filename, 'w', encoding='utf-8') as file:
        file.write(content)

@app.route('/output.m3u')
def serve_m3u_file():
    try:
        with open('output.m3u', 'r', encoding='utf-8') as file:
            content = file.read()
        return Response(content, mimetype='text/plain')
    except FileNotFoundError:
        return "The file output.m3u was not found.", 404

if __name__ == "__main__":
    main_m3u8_url = "https://gitee.com/linjiangxian/picture/raw/master/huashu.m3u"
    backup_m3u8_urls = [
        'https://raw.githubusercontent.com/ssili126/tv/main/itvlist.txt',
        "https://gitee.com/linjiangxian/picture/raw/master/shidai.m3u",
        # "https://raw.githubusercontent.com/Guovin/TV/gd/result.txt",
        # "https://raw.githubusercontent.com/maitel2020/iptv-self-use/main/iptv.m3u",
    ]
    try:
        main_m3u8_content = download_m3u8_content(main_m3u8_url)
        new_m3u8_content = check_iptv_source(main_m3u8_content, backup_m3u8_urls)
        save_m3u8_file(new_m3u8_content, "output.m3u")
        logging.info("Replacement completed. The new M3U file has been saved as 'output.m3u'.")
        app.run(host='0.0.0.0', port=5000)
    except Exception as e:
        logging.error(f"An error occurred with main URL {main_m3u8_url}: {e}")
